前幾天處理的是山頭的點資料,今天就來看看如何把有面積的水域資料加入地圖之中。
有關登山活動會使用到的水域,在 OSM 中會被標記為:
natural=water
: 天然水體landuse=reservoir
: 水庫landuse=basin
: 人工水體(蓄水池)這些都是物件的型別都是 way,所以需要在 Lua 設定檔中的 way_function
處理。
除了物件的形狀外,我們也需要有 Layer 可以顯示水域的名字,因此在 config.json
中可以多加入以下的 Layer:
// config.json
"water": {
"minzoom": 6,
"maxzoom": 14,
"simplify_below": 12,
"simplify_level": 0.0003,
"simplify_ratio": 2
},
"water_name": {
"minzoom": 14,
"maxzoom": 14
}
另外,水域的面積也是大小有別的。我們會希望在縮放層級較小的圖磚中,只包含夠大的水域。這點可以使用 tilemaker 提供的函式 way:Area()
(來源)
在開始之前,可以先查查 tilemaker 中,用於產製符合 OpenMapTiles schema 圖磚的設定檔。裡面有不少實用的自訂函式可以派上用場:
-- ZRES=Resolution for Zoom level
-- 若向量圖磚的「解析度」是 256x256,則每 pixel 是幾公尺
ZRES5 = 4891.97
ZRES6 = 2445.98
ZRES7 = 1222.99
ZRES8 = 611.5
ZRES9 = 305.7
ZRES10 = 152.9
ZRES11 = 76.4
ZRES12 = 38.2
ZRES13 = 19.1
-- 若 way 物件的面積在某一縮放層級的大小超過一個像素
-- 則開始在下一縮放層級中出現
function SetMinZoomByArea(way)
local area=way:Area()
if area>ZRES5^2 then way:MinZoom(6)
elseif area>ZRES6^2 then way:MinZoom(7)
elseif area>ZRES7^2 then way:MinZoom(8)
elseif area>ZRES8^2 then way:MinZoom(9)
elseif area>ZRES9^2 then way:MinZoom(10)
elseif area>ZRES10^2 then way:MinZoom(11)
elseif area>ZRES11^2 then way:MinZoom(12)
elseif area>ZRES12^2 then way:MinZoom(13)
else way:MinZoom(14) end
end
有了SetMinZoomByArea
這樣的函式,可以讓我們在不同縮放層級中,更有效率的篩選物件。因此相關設定檔可以撰寫為:
-- process.lua
...
-- 處理 way 物件
function way_function(way)
local isClosed = way:IsClosed() -- 封閉的 way,表示多邊形而非線段
local natural = way:Find("natural")
local landuse = way:Find("landuse")
local water = way:Find("water")
-- 設定 water 圖層
-- 篩選天然水域、水庫或人工蓄水池
if natural == "water" or landuse == "reservoir" or landuse == "basin" then
-- 若被覆蓋或不是封閉的多邊形,則不處理
if way:Find("covered") == "yes" or not isClosed then return end
-- 加入到 "water" Layer 中
way:Layer("water", true)
SetMinZoomByArea(way)
-- 出現與否有季節性
if way:Find("intermittent") == "yes" then way:Attribute("intermittent", 1) end
-- 若有名稱,則將中心點加入到 "water_name" Layer
if way:Holds("name") and natural == "water" then
way:LayerAsCentroid("water_name")
way:Attribute("name", way:Find("name"))
SetMinZoomByArea(way)
end
return -- in case we get any landuse processing
end
end
使用兩個圖層分別渲染 water
(水域) 和 water_name
(水域名稱) Layer。
有關水域,可以撰寫如下:
{
"id": "water",
"type": "fill",
"source": "taiwan",
"source-layer": "water",
"minzoom": 8,
"paint": {
"fill-color": "#BAD5FB"
}
}
有關名稱,可以撰寫如下:
{
"id": "water-name",
"type": "symbol",
"source": "taiwan",
"source-layer": "water_name",
"minzoom": 8,
"filter": [
"has",
"name"
],
"layout": {
"symbol-placement": "point",
"text-padding": 50,
"text-field": "{name}",
"text-size": 24
},
"paint": {
"text-color": "#2588b6",
"text-halo-color": "white",
"text-halo-width": 3
}
}
以日月潭為例,最終產出的成果為:
今天使用到的程式碼可以在 Github 中找到。